package com.donquijote.myspring.core;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 421527548@qq.com
 * @version V1.0
 * @title
 * @description
 * @date 2022-11-21 18:21
 */
public class ClassPathXmlApplicationContext implements ApplicationContext{

    private Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class);

    //用来存储实例化对象
    private Map<String , Object> singletonObject = new HashMap<>();

    /**
     * configLocation 是xml文件的位置
     * */
    public ClassPathXmlApplicationContext( String configLocation) {
        //解析xml文件并且实例化Bean ，将Bean 存储到 Map集合里面，使用getBean方法可以获取
        try{
            //1、对象解析的是一个输入流
            SAXReader reader = new SAXReader();
            //2、将xml文件转换成流文件：类加载器只能加载类目录下的文件，其他的取不到，所以类名叫ClassPathXmlApplicationContext
            InputStream inputStream = ClassLoader.getSystemResourceAsStream(configLocation);
            //3、解析器读取流
            Document document = reader.read(inputStream);

            //4、获取所有的Bean标签里的内容
            List<Node> beans = document.selectNodes("//bean");

            //5、lambda表达式遍历Bean标签
            beans.forEach(bean->{

                try{
                    //6、将bean给Element化，这样可以取出Bean流中的属性 id 和 class
                    Element element = (Element) bean;
                    String beanName = element.attributeValue("id");
                    String beanClassPath = element.attributeValue("class");
//                    logger.info("BeanName=" + beanName);
//                    logger.info("BeanClassPath=" + beanClassPath);
                    //7、利用反射实例化对象
                    Class<?> aClass = Class.forName(beanClassPath);
                    Object instance = aClass.getConstructor().newInstance();
                    //logger.info("newInstance=" + instance);
                    //8、将对象存入到Map集合里面
                    singletonObject.put(beanName,instance);
                }catch (Exception e){
                    e.printStackTrace();
                }
            });
            //logger.info("singletonObject = " + singletonObject);

            beans.forEach(bean->{
                try{
                    Element element = (Element) bean;
                    String beanName = element.attributeValue("id");
                    String beanClassPath = element.attributeValue("class");
                    //获取标签里的元素
                    List<Element> properties = element.elements("property");
                    properties.forEach(property->{
                        try{
//                            获取属性的名字
                            String name = property.attributeValue("name");

                            //属性Name改造成set方法写法
                            String setMethodName = "set" + name.toUpperCase().charAt(0) + name.substring(1);
                            //logger.info("属性名 = " + setMethodName);
                            //根据路径反射获取对象
                            Class<?> aClass = Class.forName(beanClassPath);
                            //根据属性名字反射获取属性
//                            Field field = aClass.getDeclaredField(name);
                            Field field = singletonObject.get(beanName).getClass().getDeclaredField(name);
                            //logger.info("fieldType = "+field.getType());
                            //反射获取set方法
                            Method setMethod = singletonObject.get(beanName).getClass().getDeclaredMethod(setMethodName,field.getType());
//                            Method setMethod = aClass.getDeclaredMethod(setMethodName,field.getType());
                            //在xml文件中获取value值
                            String value = property.attributeValue("value");
                            String ref = property.attributeValue("ref");

                            if (value != null) {
                                //获取属性简单的类型名
                                String simpleTypeName = field.getType().getSimpleName();
                                Object realValue = null;
                                switch (simpleTypeName){
                                    case "byte":
                                        realValue = Byte.parseByte(value);
                                        break;
                                    case "short":
                                        realValue = Short.parseShort(value);
                                        break;
                                    case "int":
                                        realValue = Integer.parseInt(value);
                                        break;
                                    case "long":
                                        realValue = Long.parseLong(value);
                                        break;
                                    case "float":
                                        realValue = Float.parseFloat(value);
                                        break;
                                    case "double":
                                        realValue = Double.parseDouble(value);
                                        break;
                                    case "boolean":
                                        realValue = Boolean.parseBoolean(value);
                                        break;
                                    case "char":
                                        realValue = value.charAt(0);
                                        break;
                                    case "Byte":
                                        realValue = Byte.valueOf(value);
                                        break;
                                    case "Short":
                                        realValue = Short.valueOf(value);
                                        break;
                                    case "Integer":
                                        realValue = Integer.valueOf(value);
                                        break;
                                    case "Long":
                                        realValue = Long.valueOf(value);
                                        break;
                                    case "Float":
                                        realValue = Float.valueOf(value);
                                        break;
                                    case "Double":
                                        realValue = Double.valueOf(value);
                                        break;
                                    case "Boolean":
                                        realValue = Boolean.valueOf(value);
                                        break;
                                    case "Character":
                                        realValue = Character.valueOf(value.charAt(0));
                                        break;
                                    case "String":
                                        realValue = value;
                                }

                                //说明是简单类型
                                setMethod.invoke(singletonObject.get(beanName),realValue);
                            }

                            if (ref != null) {
                                //说明是一个ref引用类型，调用set方法 使用map集合里的对象赋值
                                setMethod.invoke(singletonObject.get(beanName),singletonObject.get(ref));
                            }

                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    });
                }catch (Exception e){
                    e.printStackTrace();
                }
            });

        }catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    public Object getBean(String beanName) {
        return singletonObject.get(beanName);
    }
}
